package com.ishopping.web.manage.realm;

import java.util.List;

import com.ishopping.api.manage.entity.Role;
import com.ishopping.api.manage.entity.LoginToken;
import com.ishopping.api.manage.entity.ShiroUser;
import com.ishopping.api.manage.entity.Permission;
import com.ishopping.api.manage.enums.ResourceEnum;
import com.ishopping.api.manage.service.IPermissionService;
import com.ishopping.api.manage.service.IRoleService;
import com.ishopping.common.core.base.exception.CaptchaException;
import com.ishopping.common.core.utils.security.EncodeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.ishopping.api.manage.entity.User;
import com.ishopping.api.manage.service.IUserService;

import com.ishopping.api.manage.constants.SystemConstants;


/**
 * 安全实体数据源 - 用于获取安全实体
 * 可以是JDBC实现, 也可以是LDAP实现或者内存实现
 * 注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中需要实现自己的Realm
 * @author zhangdongping
 */
public class UserRealm extends AuthorizingRealm {
	
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private IUserService userService;

	@Autowired
	private IPermissionService permissionService;

	@Autowired
	private IRoleService roleService;
	
	/**
	 * 获取身份认证信息 - 验证用户是否拥有相应的身份
	 * 在shiro中,用户需要提供principals(身份)和credentials(证明)给shiro,从而应用能验证用户身份
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		LoginToken loginToken = (LoginToken) authcToken;
		//校验验证码是否正确
		boolean isRightCaptcha = validateCaptcha(loginToken);
		//如果验证码不正确,返回空
		if(!isRightCaptcha){
			return null;
		}

		String username = loginToken.getUsername();
		User u = new User();
		u.setUsername(username);
		User user = userService.queryOne(u);
		if(user != null){
			List<Permission> menus = permissionService.findPermissionsByUserId(user.getId(), ResourceEnum.MENU.getValue());

			ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUsername(), menus);

			byte[] salt = EncodeUtils.decodeHex(user.getSalt());

			//设置用户session
			Session session = SecurityUtils.getSubject().getSession();
			session.setAttribute(SystemConstants.CURRENT_USER, user);

			//参数1 principals: 身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可
			//参数2 credentials: 证明/凭证,即只有主体知道的安全值,如密码/数字证书等
			//参数3 credentialsSalt: 密码加盐
			//参数4 realmName: 域名称
			SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, user.getPassword(), ByteSource.Util.bytes(salt), this.getName());
			//SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, user.getPassword(), this.getName());

			//如果身份认证验证成功,返回一个AuthenticationInfo实现
			return info;
		}
		return null;
	}
	
	/**
	 * 获取授权信息,权限认证
	 * 根据用户身份获取权限信息
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取shiroUser
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		//通过用户名获取用户
		User u = new User();
		u.setUsername(shiroUser.getUsername());
		User user = userService.queryOne(u);

		//把principals放session中: key为userId, value为principals
		Subject subject = SecurityUtils.getSubject();
		Session session = subject.getSession();

		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		Long userId = user.getId();

		//查找用户具有的角色
        List<Role> roles = roleService.findRolesByUserId(userId);
        //查找用户具有的权限
		List<Permission> permissions = permissionService.findPermissionsByUserId(userId);

        //赋予角色 - 主要用于页面 <shiro:hasRole name="superAdmin">
        if(roles != null) {
            for (Role role : roles) {
                String name = role.getName();
                if (StringUtils.isNotBlank(name)) {
                    info.addRole(name);
                }
            }
        }

		//赋予权限
        if(permissions != null) {
            for (Permission permission : permissions) {
                String code = permission.getCode();
                if (StringUtils.isNotBlank(code)) {
                    info.addStringPermission(code);
                }
            }
        }
		return info;
	}

	/**
	 * 验证码校验
	 * kaptcha生成的验证码会存放到session中,所以可以直接从session中取
	 * @param token
	 * @return boolean
	 */
	protected boolean validateCaptcha(LoginToken token) {
		Subject subject = SecurityUtils.getSubject();
		Session session = subject.getSession();
		String sessionCaptcha = (String) session.getAttribute("KAPTCHA_SESSION_KEY");
		String inputCaptcha = token.getCaptcha();
		if (sessionCaptcha != null && !sessionCaptcha.equalsIgnoreCase(inputCaptcha)){
			throw new CaptchaException("验证码错误！");
		}
		return true;
	}

	/**
	 * 在权限修改后调用realm的clearCached方法清除缓存
	 * 在service中,权限修改后调用下面的方法
	 */
	public void clearCached() {
		PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
		super.clearCache(principals);
	}

}
